import json
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
import re
import datetime
import random
import numpy as np
import requests
import os


# 跨天班制的处理
'''
1. 注意现阶段的结果中没有区分主副班，通过id区分主副班。
3. 现在的结果中看不出来是否跨天。
2. 根据返回的结果画不出来跨天的图示。
'''
# 如果返回的结果的格式不同了，需要改具体的代码

# 返回的json结果中的字段名
staffId = 'staffId'
departmentId = 'departmentId'
workTimeId = 'workTimeId'
arrangeDto = 'arrangeDto'
isOverWork = 'isOverWork'
workDate = 'workDate'
workid = 'id'
# staff类中的属性中的key
work_date = 'work_date'

class Staff():
    def __init__(self, staff_id):
        self.staff_id = staff_id
        self.work_schedule = []
    def add_worktime(self, work_time):
        '''
        解析一条排班数据的信息
        work_id: 
        staff_id: 这条数据的staff编号
        work_date: 这条数据的上班日期
        c_time: 这条数据的上班时段
        is_overwork: 是否加班
        '''
        work_id = work_time[workid]
        work_date = work_time[workDate]
        workDetailDto = work_time['workDetailDto']
        for c_time in workDetailDto:
            c_time.update({'work_date' : work_date, 'work_id' : work_id})
            #is_overwork = work_time[isOverWork]
            self.work_schedule.append(c_time)


class Department():
    # 部门类，每一个部门是一个实例
    def __init__(self, department_id, shift_rule):
        '''
        初始化一个部门
        department_id: 部门编号，标识唯一部门
        staff: group信息，列表中存储了多个group，一个部门下可能有多个group
        shift_rule: 班制信息
        '''
        self.department_id = department_id
        self.shift_rule = shift_rule
        self.staff_set = []
        self.staff = {}
    # 创建一个新组，每个组有相同的部门信息和班制信息
    def add_staff(self, staff_id):
        '''
        发现了新group，将这个group加入到实例中
        staff_id: staff编号
        '''
        self.staff[staff_id] = Staff(staff_id)
        self.staff_set.append(staff_id)

    # 添加上班时间
    def add_shift(self, shift_result, show_worktime_id_set):
        '''
        shift_result: 在某个时间区间内的部门的排班结果
        show_worktime_id_set: 需要展示的时段集合
        '''
        if show_worktime_id_set == 'all':
            show_worktime_id_set = self.shift_rule.keys()
        # 遍历每一条排班结果
        for item in shift_result:
            # 如果需要显示信息的上班时段，将这条信息加入员工的上班信息中
            # if item['workTimeId'] in show_worktime_id_set:
            # 如果没有找到信息中的员工
            if not item[staffId] in self.staff_set:
                self.add_staff(item[staffId])
            self.staff[item[staffId]].add_worktime(item)


def create_deparment(shift_result, shift_rule):
    department = []
    department_info = []
    # 获取详细的部门信息
    for item in shift_result:
        department_info.append(item[departmentId])
    # 去重
    department_info = list(set(department_info))
    # 创建部门对象
    for dep_id in department_info:
        department.append(Department(dep_id, shift_rule))
    return department


def load_json(shift, filename, shift_rule):
    '''

    '''
    def add_color(shift_result, shift_rule):
        for item in shift_result:
            for c_time in item['workDetailDto']:
                c_time['color'] = shift_rule[item['workTimeId']]
            
    with open(filename, encoding='utf-8') as f:
        shift_result = json.load(f)
    # 转换班制信息
    add_color(shift_result[arrangeDto], shift_rule)
    return shift_result


def set_shift_rule_color(shift_rule):
    '''
    为每个班制设置不同的显示颜色
    '''
    def randomcolor():
        colorArr = ['1','2','3','4','5','6','7','8','9','A','B','C','D','E','F']
        color = ""
        for _ in range(6):
            color += colorArr[random.randint(0,14)]
        return "#"+color
    random.seed(100)
    t_shift = {}
    if isinstance(shift_rule, list):
        for rule in shift_rule:
            t_shift[rule] = randomcolor()
    else:
        t_shift[shift_rule] = randomcolor()
    return t_shift


def plot_gatt(staff, startdate, enddate, cnt, shift_rule):
    height = 0.8
    startdate = datetime.datetime(startdate.year,startdate.month,startdate.day,0,0,0)
    enddate = datetime.datetime(enddate.year,enddate.month,enddate.day,0,0,0)
    # 用同样的颜色画出员工的每一个上班时段
    # 还未标识加班和副班信息
    for worktime in staff.work_schedule:
        # 转换员工的上下班时间
        startCtime = worktime['startTime']
        endCtime = worktime['endTime']
        startCtime = datetime.datetime.strptime(startCtime, '%Y-%m-%d  %H:%M:%S')
        endCtime = datetime.datetime.strptime(endCtime, '%Y-%m-%d  %H:%M:%S')
        # 计算工作时长
        working_time = (endCtime - startCtime).total_seconds()
        # 计算左侧偏移量：天 + 秒
        time_diff = (startCtime - startdate)
        left = time_diff.days + 1 / (24 * 60 * 60) * time_diff.seconds
        y = cnt
        width = 1 / (24 * 60 * 60) * working_time
        color = worktime['color']
        plt.barh(y, width, left = left, height = height, color = color, alpha = 0.5)


def set_ax(fig, department, startdate, enddate, xtime_axis_format, filename, shift_rule):
    '''
    department: 部门对象
    startdate: 开始排班日期，格式："%Y-%m-%d %H:%M:%S"
    enddate: 结束排班日期，格式："%Y-%m-%d %H:%M:%S"
    '''
    def plot_grid(x, y, num, vertical = 0, interval = 1, alpha = 0.5, c = 'b', linewidth = 0.7):
        if vertical:
            for _ in range(num - 1):
                x = x + interval
                plt.plot(x, y, linestyle = '--', alpha = alpha, c = c, linewidth = linewidth)
        else:
            for _ in range(num - 1):
                y = y + interval
                plt.plot(x, y, linestyle = '--', alpha = alpha, c = c, linewidth = linewidth)
    # 计算排班间隔时间
    st = startdate
    et = enddate
    time_diff = et - st
    days = time_diff.days
    # 设置显示范围
    plt.axis([0, days, 0.5, len(department.staff_set) + 0.5])
    # 设置坐标轴标签
    plt.xlabel('schedule date')
    plt.ylabel('staff ID')
    # 更改x轴坐标显示
    time_axis = [st.strftime(xtime_axis_format)]
    for _ in range(days):
        st += datetime.timedelta(days = 1)
        time_axis.append(st.strftime(xtime_axis_format))
    x_axis = np.linspace(0, len(time_axis) - 1, len(time_axis), dtype = int)
    plt.xticks(x_axis, time_axis, rotation = 300, fontsize = 5)
    # 更改y轴坐标显示
    staff_axis = [item for item in department.staff_set]
    plt.yticks(np.linspace(1, len(department.staff_set), len(department.staff_set)), staff_axis, fontsize = 8)
    # 画网格
    # 画横轴
    plot_grid(np.array([0, len(time_axis)]), np.array([0.5, 0.5]), len(department.staff_set), vertical = 0)
    # 画竖轴
    plot_grid(np.array([0, 0]), np.array([0, len(department.staff_set) + 0.5]), len(time_axis), vertical = 1)
    # 画中午12点
    plot_grid(np.array([-0.5, -0.5]), np.array([0, len(department.staff_set) + 0.5]), len(time_axis), vertical = 1, alpha = 0.1)
    # 设置标题
    plt.title('illstration of ' + os.path.splitext(os.path.basename(filename))[0] + ' department: ' + str(department.department_id))
    # 设置图例
    patch = []
    for rule in shift_rule.keys():
        rule_name = str(rule)
        patch.append(mpatches.Patch(color = shift_rule[rule], label = 'workTimeId: ' + rule_name, alpha = 0.5))
    fig.legend(handles = patch, fontsize = 5)


def json2graph(filename = './bai/result033.json', 
                startdate = '2019-04-02 00:00:00', 
                enddate = '2019-04-14 00:00:00', 
                shift_rule = [1, 2, 3], 
                show_worktime_id_set = 'all', 
                xtime_axis_format = '%m-%d', 
                filetype = 'pdf'):
    '''
    输入:
        startdate: 开始排班日期， 格式："%Y-%m-%d %H:%M:%S"
        enddate: 结束排班日期，格式："%Y-%m-%d %H:%M:%S"
        filename: 需要解析的json文件的相对路径
        shift_rule: 排班规则，字典格式。{id: 班制信息}:key: 班制id。value: 字典，班制的上下班信息。
                    班制信息 = {'startTime': '%H:%M:%S', 'endTime': '%H:%M:%S'}
                    'startTime': 班制的上班时间
                    'endTime': 班制的下班时间
                    1. 注意现阶段的结果中没有区分主副班，通过id区分主副班。
                    2. 现阶段的value中的班制信息中只能在同一天，不能跨天。
                    3. 现在的结果中看不出来是否跨天。
        show_worktime_id_set: 需要显示的班制编号的集合，默认为全显示。
        xtime_axis_format: 时间轴的显示格式，默认：x月-x号：'%m-%d'
        filetype: 输出文件的文件格式，默认：矢量图的pdf
    输出: 
        以pdf格式输出排班结果可视化文件，输出在保存json文件的文件夹
    '''

    startdate = datetime.datetime(startdate.year,startdate.month,startdate.day,0,0,0)
    enddate = datetime.datetime(enddate.year,enddate.month,enddate.day,0,0,0)
    shift_rule = set_shift_rule_color(shift_rule)
    shift = []
    shift_result = load_json(shift, filename, shift_rule)
    # 得到部门对象 department
    shift = create_deparment(shift_result[arrangeDto], shift_rule)
    for item in shift:
        item.add_shift(shift_result[arrangeDto], show_worktime_id_set)
    # 对每一个部门画出一个显示结果
    for department in shift:
        # 创建一个fig
        fig = plt.figure()
        fig.add_subplot(111)
        set_ax(fig, department, startdate, enddate, xtime_axis_format, filename, shift_rule)
        # 画出该部门下每一个人的上班时间
        cnt = 1
        for staff_id in department.staff_set:
            plot_gatt(department.staff[staff_id], startdate, enddate, cnt, shift_rule)
            cnt += 1
        # 保存图片
        plt.savefig(os.path.splitext(filename)[0] + '_department_' + str(department.department_id) + '.' + filetype)
        # plt.show()


if __name__ == "__main__":
    filename = '算法返回.json'
    startdate = '2020-07-01 00:00:00'
    enddate = '2020-07-07 00:00:00'
    json2graph(filename, startdate, enddate, shift_rule=[135565])